package com.lixueju.security.box.web.server;

import com.alibaba.fastjson.JSONObject;
import com.lixueju.security.box.core.dto.SecurityBoxUtils;
import com.lixueju.security.box.core.enums.ResultDataEnum;
import com.lixueju.security.box.core.properties.OAuth2ClientProperties;
import com.lixueju.security.box.core.properties.SecurityBoxProperties;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;

/**
 * @author lixueju
 * @since 2019/12/8 8:25
 **/
public class SecurityBoxOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {

    private WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator();

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    SecurityBoxUtils securityBoxUtils;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthorizationServerTokenServices tokenServices;

    @Autowired
    private SecurityBoxProperties securityBoxProperties;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        try {
            //解析异常，如果是401则处理
            ResponseEntity<?> result = exceptionTranslator.translate(authException);
            if (result.getStatusCode() == HttpStatus.UNAUTHORIZED) {
                String token = request.getHeader("token");
                JSONObject jwtToken = securityBoxUtils.getJwtToken(token);
                if (jwtToken == null) {
                    Throwable cause = authException.getCause();
                    if (cause instanceof InvalidTokenException) {
                        securityBoxUtils.write(response, ResultDataEnum.AUTH_INVALID_TOKEN.code(), ResultDataEnum.AUTH_INVALID_TOKEN.desc());
                    } else {
                        securityBoxUtils.write(response, ResultDataEnum.AUTH_NONE_TOKEN.code(), ResultDataEnum.AUTH_NONE_TOKEN.desc());
                    }
                    return;
                }
                OAuth2ClientProperties client = securityBoxProperties.getOauth2().getClient();
                ClientDetails clientDetails = clientDetailsService.loadClientByClientId(client.getClientId());
                if (clientDetails == null) {
                    throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:" + client.getClientId());
                } else if (!StringUtils.equals(clientDetails.getClientSecret(), client.getClientSecret())) {
                    throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + client.getClientId());
                }
                TokenRequest tokenRequest = new TokenRequest(new HashMap<>(), client.getClientId(), clientDetails.getScope(), "security_box");
                OAuth2AccessToken refreshToken = tokenServices.refreshAccessToken(jwtToken.getString("refreshToken"), tokenRequest);
                jwtToken.put("jwtToken", refreshToken.getValue());
                jwtToken.put("refreshToken", refreshToken.getRefreshToken().getValue());
                boolean reSetJwtToken = securityBoxUtils.reSetJwtToken(token, jwtToken.toJSONString());
                if (reSetJwtToken) {
                    request.getRequestDispatcher(request.getRequestURI()).forward(request, response);
                } else {
                    securityBoxUtils.write(response, ResultDataEnum.AUTH_INVALID_TOKEN.code(), "刷新：" + ResultDataEnum.AUTH_INVALID_TOKEN.desc());
                }
            } else {
                //如果不是401异常，则以默认的方法继续处理其他异常
                super.commence(request, response, authException);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
